/*
 * This source file is part of DVGEditor.
 *
 * Copyright (c) Pavel Shchelkun 2012 DVGEditor
 * <http://code.google.com/p/dataviewer/>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "editorview.h"
#include "editorscene.h"
#include <QFileInfo>
#include <QWheelEvent>
#include <QMouseEvent>
#include <QGraphicsRectItem>
#include <QtSvg/QGraphicsSvgItem>
#include <QPaintEvent>
#include <QMessageBox>
#include <qmath.h>
#include <QDebug>
#include <QScrollArea>
#include <QScrollBar>

#ifndef QT_NO_OPENGL
#include <QGLWidget>
#endif

EditorView::EditorView(QGraphicsScene *scene, QWidget *parent) :
    QGraphicsView(scene, parent)
{
    m_documentItem   = NULL;
    m_backgroundItem = NULL;
    m_outlineItem    = NULL;
    m_LeftMouseButtonPressed = false;
    m_HandMode = false;

#ifndef QT_NO_OPENGL
    m_renderer = OpenGL;
#else
    m_renderer = Native;
#endif

    setRenderer(m_renderer);
    setHighQualityAntialiasing(true);

    //setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    //setTransformationAnchor(AnchorUnderMouse);
    setResizeAnchor(QGraphicsView::AnchorUnderMouse);
    //setDragMode(ScrollHandDrag);
    setViewportUpdateMode(FullViewportUpdate);

    QPixmap tilePixmap(64, 64);
    tilePixmap.fill(Qt::white);
    QPainter tilePainter(&tilePixmap);
    QColor color(220, 220, 220);
    tilePainter.fillRect(0, 0, 32, 32, color);
    tilePainter.fillRect(32, 32, 32, 32, color);
    tilePainter.end();

    setBackgroundBrush(tilePixmap);
}

void EditorView::drawBackground(QPainter *p, const QRectF &)
{
    p->save();
    p->resetTransform();
    p->drawTiledPixmap(viewport()->rect(), backgroundBrush().texture());
    p->restore();
}

void EditorView::loadBackgroundImage(const QString &_file)
{
    QFile file(_file);
    QFileInfo finfo(file);

    if (!file.exists())
    {
        return;
    }

    EditorScene *s = dynamic_cast<EditorScene*>(scene());

    bool drawBackground = (m_backgroundItem ? m_backgroundItem->isVisible() : false);
    bool drawOutline = (m_outlineItem ? m_outlineItem->isVisible() : true);

    if (m_documentItem)
    {
        s->removeItem(m_documentItem);
        delete m_documentItem;
        m_documentItem = NULL;
    }
    resetTransform();

    if (finfo.suffix().toUpper() == "SVG")
    {
        m_documentItem = new QGraphicsSvgItem(file.fileName());
    }
    else
    {
        m_documentItem = new QGraphicsPixmapItem(QPixmap(file.fileName()));
    }


    m_documentItem->setFlags(QGraphicsItem::ItemClipsToShape);
    m_documentItem->setCacheMode(QGraphicsItem::NoCache);
    m_documentItem->setZValue(0);

    if (m_backgroundItem)
    {
        s->removeItem(m_backgroundItem);
        delete m_backgroundItem;
        m_backgroundItem = NULL;
    }

    m_backgroundItem = new QGraphicsRectItem(m_documentItem->boundingRect());
    m_backgroundItem->setBrush(Qt::white);
    m_backgroundItem->setPen(Qt::NoPen);
    m_backgroundItem->setVisible(drawBackground);
    m_backgroundItem->setZValue(-1);

    if (m_outlineItem)
    {
        s->removeItem(m_outlineItem);
        delete m_outlineItem;
        m_outlineItem = NULL;
    }

    m_outlineItem = new QGraphicsRectItem(m_documentItem->boundingRect());
    QPen outline(Qt::black, 1, Qt::SolidLine);
    outline.setCosmetic(true);
    m_outlineItem->setPen(outline);
    m_outlineItem->setBrush(Qt::NoBrush);
    m_outlineItem->setVisible(drawOutline);
    m_outlineItem->setZValue(1);

    s->addItem(m_backgroundItem);
    s->addItem(m_documentItem);
    s->addItem(m_outlineItem);

    QRectF sceneRect(m_outlineItem->boundingRect().adjusted(-10, -10, 10, 10));
    s->setSceneRect(sceneRect);

    qreal centerX = sceneRect.right() - sceneRect.left();
    if (centerX)
    {
        centerX /= 2;
    }
    centerX += sceneRect.left();
    qreal centerY = sceneRect.bottom() - sceneRect.top();
    if (centerY)
    {
        centerY /= 2;
    }
    centerY += sceneRect.top();

    SetCenter(QPointF(centerX, centerY));

    s->setDocumentRect(m_documentItem->boundingRect());

    s->setDocumentColor(QColor(Qt::white));
}

void EditorView::createBackgroundImage(int width, int height, const QColor &color)
{
    EditorScene *s = dynamic_cast<EditorScene*>(scene());

    bool drawBackground = (m_backgroundItem ? m_backgroundItem->isVisible() : false);
    bool drawOutline = (m_outlineItem ? m_outlineItem->isVisible() : true);

    if (m_documentItem)
    {
        s->removeItem(m_documentItem);
        delete m_documentItem;
        m_documentItem = NULL;
    }
    resetTransform();

    QPixmap pixmap(QSize(width,height));
    pixmap.fill(color);

    m_documentItem = new QGraphicsPixmapItem(pixmap);


    m_documentItem->setFlags(QGraphicsItem::ItemClipsToShape);
    m_documentItem->setCacheMode(QGraphicsItem::NoCache);
    m_documentItem->setZValue(0);

    if (m_backgroundItem)
    {
        s->removeItem(m_backgroundItem);
        delete m_backgroundItem;
        m_backgroundItem = NULL;
    }

    m_backgroundItem = new QGraphicsRectItem(m_documentItem->boundingRect());
    m_backgroundItem->setBrush(Qt::white);
    m_backgroundItem->setPen(Qt::NoPen);
    m_backgroundItem->setVisible(drawBackground);
    m_backgroundItem->setZValue(-1);

    if (m_outlineItem)
    {
        s->removeItem(m_outlineItem);
        delete m_outlineItem;
        m_outlineItem = NULL;
    }

    m_outlineItem = new QGraphicsRectItem(m_documentItem->boundingRect());
    QPen outline(Qt::black, 1, Qt::SolidLine);
    outline.setCosmetic(true);
    m_outlineItem->setPen(outline);
    m_outlineItem->setBrush(Qt::NoBrush);
    m_outlineItem->setVisible(drawOutline);
    m_outlineItem->setZValue(1);

    s->addItem(m_backgroundItem);
    s->addItem(m_documentItem);
    s->addItem(m_outlineItem);

    QRectF sceneRect(m_outlineItem->boundingRect().adjusted(-10, -10, 10, 10));
    s->setSceneRect(sceneRect);

    qreal centerX = sceneRect.right() - sceneRect.left();
    if (centerX)
    {
        centerX /= 2;
    }
    centerX += sceneRect.left();
    qreal centerY = sceneRect.bottom() - sceneRect.top();
    if (centerY)
    {
        centerY /= 2;
    }
    centerY += sceneRect.top();

    SetCenter(QPointF(centerX, centerY));

    s->setDocumentRect(m_documentItem->boundingRect());
    s->setDocumentColor(color);
}

bool EditorView::isExistGraphicsItem(QGraphicsItem *item)
{
    if (item == m_backgroundItem ||
        item == m_documentItem ||
        item == m_outlineItem)
    {
        return true;
    }
    return false;
}

void EditorView::setRenderer(EditorView::RendererType type)
{
    m_renderer = type;

    if (m_renderer == OpenGL)
    {
#ifndef QT_NO_OPENGL
        setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
#endif
    }
    else
    {
        setViewport(new QWidget);
    }
}

void EditorView::setHighQualityAntialiasing(bool highQualityAntialiasing)
{
#ifndef QT_NO_OPENGL
    setRenderHint(QPainter::HighQualityAntialiasing, highQualityAntialiasing);
#else
    Q_UNUSED(highQualityAntialiasing);
#endif
}

void EditorView::setViewBackground(bool enable)
{
    if (!m_backgroundItem)
    {
        return;
    }
    m_backgroundItem->setVisible(enable);
}

void EditorView::setViewOutline(bool enable)
{
    if (!m_outlineItem)
    {
        return;
    }
    m_outlineItem->setVisible(enable);
}

void EditorView::wheelEvent(QWheelEvent *event)
{
    //Get the position of the mouse before scaling, in scene coords
    QPointF pointBeforeScale(mapToScene(event->pos()));

    //Get the original screen centerpoint
    QPointF screenCenter = GetCenter(); //CurrentCenterPoint; //(visRect.center());

    //Scale the view ie. do the zoom
    double scaleFactor = 1.15; //How fast we zoom
    if(event->delta() > 0)
    {
        //Zoom in
        scale(scaleFactor, scaleFactor);
    }
    else
    {
        //Zooming out
        scale(1.0 / scaleFactor, 1.0 / scaleFactor);
    }

    //Get the position after scaling, in scene coords
    QPointF pointAfterScale(mapToScene(event->pos()));

    //Get the offset of how the screen moved
    QPointF offset = pointBeforeScale - pointAfterScale;

    //Adjust to the new center for correct zooming
    QPointF newCenter = screenCenter + offset;
    SetCenter(newCenter);

    event->accept();

    QGraphicsView::wheelEvent(event);
}

void EditorView::paintEvent(QPaintEvent *event)
{
    if (m_renderer == Image)
    {
        if (m_image.size() != viewport()->size())
        {
            m_image = QImage(viewport()->size(), QImage::Format_ARGB32_Premultiplied);
        }

        QPainter imagePainter(&m_image);
        QGraphicsView::render(&imagePainter);
        imagePainter.end();

        QPainter p(viewport());
        p.drawImage(0, 0, m_image);
    }
    else
    {
        QGraphicsView::paintEvent(event);
    }
}

void EditorView::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        m_LeftMouseButtonPressed = true;
        if (!m_HandMode)
        {
            QGraphicsView::mousePressEvent(event);
        }
        else
        {
            LastPanPoint = event->pos();
            m_LastLButtonPressedPoint = event->pos();
            setCursor(Qt::ClosedHandCursor);
        }
    }
    else
    {
        QGraphicsView::mousePressEvent(event);
    }
}

void EditorView::mouseMoveEvent(QMouseEvent *event)
{    
    QPointF delta(0.0,0.0);
    if(!LastPanPoint.isNull())
    {
        //Get how much we panned
        delta = mapToScene(event->pos()) - mapToScene(LastPanPoint);
        LastPanPoint = event->pos();
    }
    if (!m_HandMode)
    {
        QGraphicsView::mouseMoveEvent(event);
    }
    else
    {
        if (m_LeftMouseButtonPressed)
        {
            //Update the center ie. do the pan
            //SetCenter(GetCenter() - delta);
            QPoint offset = event->pos() - m_LastLButtonPressedPoint;
            m_LastLButtonPressedPoint = event->pos();
            int hvalue = horizontalScrollBar()->value();
            int vvalue = verticalScrollBar()->value();
            horizontalScrollBar()->setValue(hvalue - int(offset.x() + 0.5));
            verticalScrollBar()->setValue(vvalue - int(offset.y() + 0.5));
        }
    }
}

void EditorView::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        m_LeftMouseButtonPressed = false;
        if (!m_HandMode)
        {
            QGraphicsView::mouseReleaseEvent(event);
        }
        else
        {
            setCursor(Qt::OpenHandCursor);
            LastPanPoint = QPoint();
            m_LastLButtonPressedPoint = QPoint();
        }
    }
    else
    {
        QGraphicsView::mouseReleaseEvent(event);
    }
}

void EditorView::resizeEvent(QResizeEvent *event)
{
    //Get the rectangle of the visible area in scene coords
    QRectF visibleArea = mapToScene(rect()).boundingRect();
    SetCenter(visibleArea.center());

    //Call the subclass resize so the scrollbars are updated correctly
    QGraphicsView::resizeEvent(event);
}

void EditorView::SetCenter(const QPointF &centerPoint)
{
    //Get the rectangle of the visible area in scene coords
    QRectF visibleArea = mapToScene(rect()).boundingRect();

    //Get the scene area
    QRectF sceneBounds = sceneRect();

    double boundX = visibleArea.width() / 2.0;
    double boundY = visibleArea.height() / 2.0;
    double boundWidth = sceneBounds.width() - 2.0 * boundX;
    double boundHeight = sceneBounds.height() - 2.0 * boundY;

    //The max boundary that the centerPoint can be to
    QRectF bounds(boundX, boundY, boundWidth, boundHeight);
//    qDebug() << boundX;
//    qDebug() << boundY;
//    qDebug() << boundWidth;
//    qDebug() << boundHeight;

    if(bounds.contains(centerPoint)) {
        //We are within the bounds
        CurrentCenterPoint = centerPoint;
    } else {
        //qDebug() << centerPoint.x() << " " << centerPoint.y();
        //We need to clamp or use the center of the screen
        if(visibleArea.contains(sceneBounds)) {
            //Use the center of scene ie. we can see the whole scene
            CurrentCenterPoint = sceneBounds.center();
        } else {
            //qDebug() << centerPoint.x() << " " << centerPoint.y();
            CurrentCenterPoint = centerPoint;

            //We need to clamp the center. The centerPoint is too large
            if(centerPoint.x() > bounds.x() + bounds.width()) {
                CurrentCenterPoint.setX(bounds.x() + bounds.width());
            } else if(centerPoint.x() < bounds.x()) {
                CurrentCenterPoint.setX(bounds.x());
            }

            if(centerPoint.y() > bounds.y() + bounds.height()) {
                CurrentCenterPoint.setY(bounds.y() + bounds.height());
            } else if(centerPoint.y() < bounds.y()) {
                CurrentCenterPoint.setY(bounds.y());
            }
            //qDebug() << CurrentCenterPoint.x() << " " << CurrentCenterPoint.y();
        }
    }

    //Update the scrollbars
    centerOn(CurrentCenterPoint);
}

void EditorView::setHandMode(bool hand)
{
    m_HandMode = hand;
    if (hand)
        setCursor(Qt::OpenHandCursor);
    else
        setCursor(Qt::ArrowCursor);
}
















